package com.github.data_manage.service

import com.github.data_manage.config.SystemException
import com.github.data_manage.dao.*
import com.github.data_manage.domain.dto.PersonRelationDto
import com.github.data_manage.domain.po.*
import com.github.data_manage.domain.vo.*
import com.github.data_manage.util.TimeUtils
import jakarta.transaction.Transactional
import kotlinx.coroutines.async
import kotlinx.coroutines.runBlocking
import org.springframework.data.domain.Page
import org.springframework.data.domain.PageRequest
import org.springframework.data.jpa.domain.Specification
import org.springframework.stereotype.Service
import java.time.LocalDate
import java.time.LocalDateTime

/**
 *@author CHN
 *@since 2024-08-08 21:46
 */
@Service
class PersonService(
    private val companyDao: CompanyDao,
    private val personsDao: PersonsDao,
    private val schoolDao: SchoolDao,
    private val personWorkExperienceDao: PersonWorkExperienceDao,
    private val personInvestmentExperienceDao: PersonInvestmentExperienceDao,
    private val personEducationExperienceDao: PersonEducationExperienceDao,
    private val fundPersonDao: FundPersonDao,
    private val companyShareholderDao: CompanyShareholderDao,
    private val fundDao: FundDao,
    private val investmentsDao: InvestmentsDao
) {

    @SuppressWarnings("all")
    @Transactional(rollbackOn = [Exception::class])
    fun saveOrUpdatePerson(personModifyVo: PersonModifyVo) {
        val persons = personModifyVo.persons
        if (persons.personId == null) {
            persons.createTime = LocalDateTime.now()
        }
        personsDao.save(persons)
        fundPersonDao.updatePersonNameById(
            persons.personId!!,
            persons.name!!,
            persons.englishName,
        )
        companyShareholderDao.updatePersonName(persons.personId!!, persons.name!!)
        persons.friends?.forEach { friend ->
            if (friend.personId == null) {
                val newPerson = Persons()
                newPerson.createTime = LocalDateTime.now()
                newPerson.name = friend.name
                personsDao.save(newPerson)
                friend.personId = newPerson.personId
            }
        }
        personsDao.save(persons)
        personWorkExperienceDao.removeAllByPersonIdEquals(persons.personId!!)
        personEducationExperienceDao.removeAllByPersonIdEquals(persons.personId!!)
        personModifyVo.personWorkExperiences?.forEach { personWorkExperience ->
            // 公司id为空就根据名字去找一下是否已存在,如果不存在就新增
            if (personWorkExperience.companyId == null) {
                companyDao.findCompanyByCompanyNameEquals(personWorkExperience.companyName)?.let { dbCompany ->
                    personWorkExperience.companyId = dbCompany.companyId
                } ?: run {
                    val company = Company()
                    company.companyName = personWorkExperience.companyName
                    companyDao.save(company)
                    personWorkExperience.companyId = company.companyId
                }
            }
            if (personWorkExperience.startDate != null && personWorkExperience.endDate == null) {
                persons.currentCompany = personWorkExperience.companyName
                persons.currentCompanyId = personWorkExperience.companyId
                persons.currentCompanyPosition = personWorkExperience.position
                persons.currentCompanyInTime = personWorkExperience.startDate
            }
            // 同事没有id就新增,需要一并新增工作经历
            personWorkExperience.leaders?.forEach { personColleague ->
                if (personColleague.personId == null) {
                    val newPerson = Persons()
                    newPerson.createTime = LocalDateTime.now()
                    newPerson.name = personColleague.name
                    personsDao.save(newPerson)
                    personColleague.personId = newPerson.personId
                    val newPersonWorkExperience = PersonWorkExperience()
                    newPersonWorkExperience.companyId = personWorkExperience.companyId
                    newPersonWorkExperience.companyName = personWorkExperience.companyName
                    newPersonWorkExperience.address = personWorkExperience.address
                    newPersonWorkExperience.personId = newPerson.personId
                    personWorkExperienceDao.save(newPersonWorkExperience)
                } else {
                    // 如果没有该公司的工作经历就加一个
                    if (personWorkExperienceDao.countByPersonIdAndCompanyId(
                            personColleague.personId!!,
                            personWorkExperience.companyId!!
                        ) == 0
                    ) {
                        val newPersonWorkExperience = PersonWorkExperience()
                        newPersonWorkExperience.companyId = personWorkExperience.companyId
                        newPersonWorkExperience.companyName = personWorkExperience.companyName
                        newPersonWorkExperience.address = personWorkExperience.address
                        newPersonWorkExperience.personId = personColleague.personId
                        personWorkExperienceDao.save(newPersonWorkExperience)
                    }
                }
            }
            personWorkExperience.underlings?.forEach { personColleague ->
                if (personColleague.personId == null) {
                    val newPerson = Persons()
                    newPerson.createTime = LocalDateTime.now()
                    newPerson.name = personColleague.name
                    personsDao.save(newPerson)
                    personColleague.personId = newPerson.personId
                    val newPersonWorkExperience = PersonWorkExperience()
                    newPersonWorkExperience.companyId = personWorkExperience.companyId
                    newPersonWorkExperience.companyName = personWorkExperience.companyName
                    newPersonWorkExperience.address = personWorkExperience.address
                    newPersonWorkExperience.personId = newPerson.personId
                    personWorkExperienceDao.save(newPersonWorkExperience)
                } else {
                    // 如果没有该公司的工作经历就加一个
                    if (personWorkExperienceDao.countByPersonIdAndCompanyId(
                            personColleague.personId!!,
                            personWorkExperience.companyId!!
                        ) == 0
                    ) {
                        val newPersonWorkExperience = PersonWorkExperience()
                        newPersonWorkExperience.companyId = personWorkExperience.companyId
                        newPersonWorkExperience.companyName = personWorkExperience.companyName
                        newPersonWorkExperience.address = personWorkExperience.address
                        newPersonWorkExperience.personId = personColleague.personId
                        personWorkExperienceDao.save(newPersonWorkExperience)
                    }
                }

            }
            personWorkExperience.personId = persons.personId
            personWorkExperience.id = null
            personWorkExperienceDao.save(personWorkExperience)
        }
        personModifyVo.personEducationExperiences?.forEach { personEducationExperience ->
            // 学校id为空就根据名字去找一下是否已存在,如果不存在就新增
            if (personEducationExperience.schoolId == null) {
                schoolDao.findSchoolBySchoolNameEquals(personEducationExperience.schoolName)?.let { dbSchool ->
                    personEducationExperience.schoolId = dbSchool.id
                } ?: run {
                    val school = School()
                    school.schoolName = personEducationExperience.schoolName
                    schoolDao.save(school)
                    personEducationExperience.schoolId = school.id
                }
            }
            personEducationExperience.personId = persons.personId
            personEducationExperience.id = null
            personEducationExperienceDao.save(personEducationExperience)
        }
        // 最高学历就是结束日期最短的或者没有结束日期的
        personModifyVo.personEducationExperiences?.filter { it.endDate != null }?.maxByOrNull { it.endDate!! }
            ?.let { notNullMaxEndDateInfo ->
                persons.highestSchool = notNullMaxEndDateInfo.schoolName
                persons.highestSchoolId = notNullMaxEndDateInfo.schoolId
            } ?: run {
            personModifyVo.personEducationExperiences?.firstOrNull { it.endDate == null }?.let { mullMaxEndDateInfo ->
                persons.highestSchool = mullMaxEndDateInfo.schoolName
                persons.highestSchoolId = mullMaxEndDateInfo.schoolId
            }
        }
        personsDao.save(persons)
        persons.takeIf { it.currentCompanyId != null }?.let {
            fundPersonDao.updatePersonPositionByPersonIdAndCompanyId(
                persons.personId!!,
                persons.currentCompanyId!!,
                persons.currentCompanyPosition
            )
        }

    }

    fun getPersonDetailsById(personId: Int): PersonModifyVo? {
        return personsDao.findById(personId).map { persons ->
            val personModifyVo = PersonModifyVo()
            personModifyVo.persons = persons
            personModifyVo.personWorkExperiences = personWorkExperienceDao.findAllByPersonIdEquals(personId)
            personModifyVo.personInvestmentExperiences = personInvestmentExperienceDao.findAllByPersonIdEquals(personId)
            personModifyVo.personEducationExperiences = personEducationExperienceDao.findAllByPersonIdEquals(personId)
            personModifyVo
        }.orElse(null)
    }

    fun findAll(condition: Specification<Persons>, page: PageRequest): Page<Persons> {
        return personsDao.findAll(condition, page)
    }

    @Transactional(rollbackOn = [Exception::class])
    fun deletePersonsByIds(personIds: Collection<Int>) {
        for (personId in personIds) {
            personsDao.findById(personId).ifPresent { person ->
                personsDao.delete(person)
                personWorkExperienceDao.removeAllByPersonIdEquals(personId)
                personInvestmentExperienceDao.removeAllByPersonIdEquals(personId)
                personEducationExperienceDao.removeAllByPersonIdEquals(personId)
                fundPersonDao.deleteAllByPersonIdEquals(personId)
            }
        }
    }

    fun save(person: Persons) {
        personsDao.save(person)
    }

    fun personRelation(personId: Int): PersonRelationVo {
        val persons = personsDao.findById(personId).orElseThrow { SystemException("成员不存在") }
        return runBlocking {

            val fundRelations = async {
                fundPersonDao.findAllByPersonIdEquals(personId).map { fundPerson ->
                    fundDao.findById(fundPerson.fundId!!)
                        .map {
                            PersonFundRelationVo(
                                fundPerson.fundId!!,
                                it.fundName!!,
                                fundPerson.position,
                                fundPerson.startDate,
                                fundPerson.endDate
                            )
                        }
                        .orElseThrow { SystemException("基金信息不存在") }
                }
            }
            val investmentRelations = async {
                personInvestmentExperienceDao.findAllByPersonIdEquals(personId)
                    .map { personInvestmentExperience ->
                        val investments = investmentsDao.findById(personInvestmentExperience.investmentId!!)
                            .orElseThrow { SystemException("投资案例不存在") }
                        PersonInvestmentRelationVo(
                            investments.id!!, investments.name!!, investments.aum,
                            personInvestmentExperience.scale, personInvestmentExperience.amount,
                            personInvestmentExperience.startInvestTime, personInvestmentExperience.updateTime
                        )
                    }
            }

            val friends = persons.friends ?: emptyList()


            val colleagues = async {
                personWorkExperienceDao.findAllByPersonIdEquals(personId).map { personWorkExperience ->
                    val colleaguesPersons = personWorkExperienceDao.findColleagues(
                        personWorkExperience.companyId!!,
                        personId,
//                        personWorkExperience.startDate,
//                        personWorkExperience.endDate,
                    ).map { colleaguesWorkExperience ->
                        val colleague = personsDao.findById(colleaguesWorkExperience.personId!!)
                            .orElseThrow { SystemException("成员未找到") }
                        val level =
                            if (TimeUtils.isBetween(
                                    personWorkExperience.startDate,
                                    personWorkExperience.endDate,
                                    colleaguesWorkExperience.startDate,
                                    colleaguesWorkExperience.endDate
                                )
                            ) {
                                1
                            } else {
                                2
                            }
                        PersonColleagueRelationVo(
                            colleague.personId!!,
                            colleague.name!!,
                            colleaguesWorkExperience.startDate,
                            colleaguesWorkExperience.endDate,
                            colleague.note,
                            level
                        )
                    }
                    PersonCompanyRelationVo(
                        personWorkExperience.companyId!!, personWorkExperience.companyName,
                        personWorkExperience.position, personWorkExperience.address,
                        personWorkExperience.startDate, personWorkExperience.endDate,
                        personWorkExperience.note,
                        colleaguesPersons
                    )
                }
            }

            val schoolMates = async {
                personEducationExperienceDao.findAllByPersonIdEquals(personId).map { personEducationExperience ->
                    val schoolMatesPersons = personEducationExperienceDao.findSchoolMates(
                        personEducationExperience.personId!!,
                        personEducationExperience.schoolId!!,
//                        personEducationExperience.startDate,
//                        personEducationExperience.endDate
                    ).map { schoolMatesEducationExperience ->
                        val schoolMate = personsDao.findById(schoolMatesEducationExperience.personId!!)
                            .orElseThrow { SystemException("成员未找到") }
                        val level =
                            if (TimeUtils.isBetween(
                                    personEducationExperience.startDate,
                                    personEducationExperience.endDate,
                                    schoolMatesEducationExperience.startDate,
                                    schoolMatesEducationExperience.endDate
                                )
                            ) {
                                1
                            } else {
                                2
                            }
                        PersonSchoolMateRelationVo(
                            schoolMatesEducationExperience.personId!!,
                            schoolMate.name!!,
                            personEducationExperience.specialized,
                            personEducationExperience.startDate,
                            personEducationExperience.endDate,
                            personEducationExperience.note,
                            level
                        )
                    }
                    PersonSchoolRelationVo(
                        personEducationExperience.schoolId!!, personEducationExperience.schoolName,
                        personEducationExperience.specialized, personEducationExperience.startDate,
                        personEducationExperience.endDate,
                        schoolMatesPersons
                    )
                }
            }

            PersonRelationVo(
                personId,
                persons.name!!,
                fundRelations.await(),
                investmentRelations.await(),
                friends,
                colleagues.await(),
                schoolMates.await()
            )
        }


    }
}
